﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LibreriaAcceso.Clases;

namespace LibreriaAcceso
{
    public static class Passwords
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parametros"></param>
        /// <returns></returns>
        public static string Genera(ParametrosPassword parametros)
        {
            #region Verificaciones
            int longitudTotal =parametros.MinimoMinusculas + parametros.MinimoMayusculas + parametros.MinimoSimbolos + parametros.MinimoNumeros;
            if (parametros.MaximoLongitud != 0 && longitudTotal > parametros.MaximoLongitud)
            {
                throw new ExcepciondeLibreriaAcceso("Los mínimos superan el máximo de longitud, favor de verificar");
            }
            if (parametros.MaximoLongitud != 0 && parametros.MaximoLongitud < parametros.MinimoLongitud)
            {
                throw new ExcepciondeLibreriaAcceso("El mínimo de longitud supera al máximo de longitud, favor de verificar");
            }
           
            if (parametros.MaximoSimbolos != 0 && parametros.MaximoSimbolos < parametros.MinimoSimbolos)
            {
                throw new ExcepciondeLibreriaAcceso("El mínimo de simbolos es mayor al máximo de simbolos, favor de verificar");
            }

            if (parametros.MaximoNumeros != 0 && parametros.MaximoNumeros < parametros.MinimoNumeros)
            {
                throw new ExcepciondeLibreriaAcceso("El mínimo de caracteres numericos al máximo de caracteres numericos, favor de verificar");
            }
            if (parametros.SimbolosProhibidos.Where(x => parametros.ListaDeSimbolos.Contains(x)).FirstOrDefault() != null)
            {
                throw new ExcepciondeLibreriaAcceso("La lista de símbolos contiene símbolos inválidos, favor de verificar");
            }
            if (parametros.MinimoSimbolos != 0 && parametros.ListaDeSimbolos.Count==0)
            {
                throw new ExcepciondeLibreriaAcceso("No hay ningún símbolo a insertar en la contraseña aunque son requeridos al menos " + parametros.MinimoSimbolos+", favor de verificar");
            }
            #endregion Verificaciones

            string contrasenaFinal = "";
            bool continuaElCiclo = true;
            Random rng = new Random();
            int numSimbolos = 0;
            int numMayusculas = 0;
            int numMinusculas = 0;
            int numNumeros = 0;
            #region CicloQueGenera
            while(continuaElCiclo)
            {
                string newChar = "";
                int numEscoger=0;
                #region CicloQueEscogeCaracter

                if (numMayusculas < parametros.MinimoMayusculas)
                {

                    newChar = GetLetter().ToUpper();
                    numMayusculas++;
                }
                else if (numMinusculas < parametros.MinimoMinusculas)
                {

                    newChar = GetLetter();
                    numMinusculas++;
                }
                else if (numNumeros < parametros.MinimoNumeros)
                {
                    newChar =rng.Next(0, 10).ToString();
                    numNumeros++;
                }
                else if (numSimbolos < parametros.MinimoSimbolos)
                {
                    numEscoger = rng.Next(0, parametros.ListaDeSimbolos.Count);
                    newChar = parametros.ListaDeSimbolos[numEscoger];
                    numSimbolos++;
                }
                else
                {
                    //cuando los minimos ya son estan completos
                    while (newChar.Equals(""))
                    {
                        numEscoger = rng.Next(0, 3);
                        switch (numEscoger)
                        {
                            case 0:
                                if (parametros.MaximoSimbolos==0 || numSimbolos < parametros.MaximoSimbolos)
                                {
                                    numEscoger = rng.Next(0, parametros.ListaDeSimbolos.Count);
                                    newChar = parametros.ListaDeSimbolos[numEscoger];
                                    numSimbolos++;
                                }
                                break;
                            case 1:
                                if (parametros.MaximoNumeros==0 || numNumeros < parametros.MaximoNumeros)
                                {
                                    newChar = rng.Next(0, 10).ToString();
                                    numNumeros++;
                                }
                                break;
                            case 2: 
                                numEscoger = rng.Next(0, 2);
                                newChar = GetLetter();
                                if(numEscoger==1)
                                {
                                    newChar = newChar.ToUpper();
                                }
                                break;
                        }
                    }
                }
                #endregion CicloQueEscogeCaracter


                contrasenaFinal += newChar;
                if (contrasenaFinal.Length >= parametros.MinimoLongitud)
                {
                    continuaElCiclo = false;
                    //si el maximo es diferente de 0 y la longitud de la contraseña final es menor al maximo y si el aleatorio sale =1 continua el ciclo
                    if (parametros.MaximoLongitud != 0 && contrasenaFinal.Length < parametros.MaximoLongitud && rng.Next(0, 2) == 1)
                    {
                        continuaElCiclo = true;
                    }
                }

            }
            #endregion CicloQueGenera
            return contrasenaFinal.Shuffle();
        }
        /// <summary>
        /// Metodo que verica la validez de un string con respecto a los parametros,
        /// el unico maximo que se toma es la longitud del string, que si es 0 no se considera.
        /// </summary>
        /// <param name="parametros"></param>
        /// <returns></returns>
        public static bool Verifica(ParametrosPassword parametros, string input)
        {
            int numMinusculas = 0;
            int numMayusculas = 0;
            int numSimbolos = 0;
            int numNumeros = 0;
            if(parametros.MinimoLongitud>input.Length && (parametros.MaximoLongitud!=0 && parametros.MaximoLongitud<input.Length))
                return false;
            for (int i = 0; i < input.Length; i++)
            {
                char caracter = input[i];
                if (Char.IsLetter(caracter))
                {
                    if (Char.IsUpper(caracter))
                        numMayusculas++;
                    else
                        numMinusculas++;
                }
                else if (Char.IsNumber(caracter))
                {
                    numNumeros++;
                }
                else if (parametros.ListaDeSimbolos.Contains(caracter.ToString()))
                    numSimbolos++;
                 
            }
            if (parametros.MinimoMayusculas != 0 && parametros.MinimoMayusculas > numMayusculas)
                return false;
            if (parametros.MinimoMinusculas != 0 && parametros.MinimoMinusculas > numMinusculas)
                return false;
            if (parametros.MinimoNumeros != 0 && parametros.MinimoNumeros > numNumeros)
                return false;
            if (parametros.MinimoSimbolos != 0 && parametros.MinimoSimbolos > numSimbolos)
                return false;

            return true;
        }
        /// <summary>
        /// Revuelve de manera aleatoria el string del password para crear uno relativamente seguro.
        /// </summary>
        /// <param name="str">el password</param>
        /// <returns>el password revuelto</returns>
        public static string Shuffle(this string str)
        {
            char[] array = str.ToCharArray();
            Random rng = new Random();
            int n = array.Length;
            while (n > 1)
            {
                n--;
                int k = rng.Next(n + 1);
                var value = array[k];
                array[k] = array[n];
                array[n] = value;
            }
            return new string(array);
        }
        /// <summary>
        /// Método que devuelve una letra del abecedario de manera aleatoria.
        /// </summary>
        /// <returns></returns>
        private static string GetLetter()
        {
            Random rng = new Random();
            int num = rng.Next(0, 26); // Zero to 25
            string let = ((char)('a' + num)).ToString();
           
            return let;
        }

    }
}
